Speed up commutation-aware rewrite searches - #5298
Conversation
Build a lazy block-local interaction index keyed by proven logical qubit identity. Segmented streams let find_nearest skip unrelated quantum operations while preserving the existing SSA frontier, endpoint predicates, conservative barriers, and physical fallback for unresolved anchors. Keep index state private to CommutationAnalysis and maintain or discard it through the existing rewrite listener. Reuse normalized anchor support after construction and avoid indexing work on adjacent or otherwise inexpensive searches. Verified with the focused CommutationAwareRewrite unit suite, QuakeSimplify commutation lit coverage, the verifier-enabled linear-value pipeline, public issue reproducers, FTQC hotspots, and NISQ regression guards. Signed-off-by: Thomas Alexander <talexander@nvidia.com>
95790e1 to
d4aace7
Compare
CI Summary (
|
| Job | Result |
|---|---|
binaries |
⏩ skipped |
build_and_test |
✅ success |
changes |
✅ success |
config_devdeps |
✅ success |
config_source_build |
⏩ skipped |
config_wheeldeps |
✅ success |
devdeps |
✅ success |
docker_image |
⏩ skipped |
gen_code_coverage |
⏩ skipped |
metadata |
✅ success |
python_metapackages |
⏩ skipped |
python_wheels |
⏩ skipped |
source_build |
⏩ skipped |
wheeldeps |
✅ success |
⏩ Skipped jobs (7) — intentionally skipped on PR builds; run on merge_group / workflow_dispatch
| Job |
|---|
binaries |
config_source_build |
docker_image |
gen_code_coverage |
python_metapackages |
python_wheels |
source_build |
All sub-jobs (43) — every matrix leg, with links
| Job | Status | Link |
|---|---|---|
| Build and test (amd64, gcc12, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| CI Summary | ❔ in_progress | view |
| Check for stable CUDA-Q changes | ✅ success | view |
| Configure build (devdeps) | ✅ success | view |
| Configure build (source_build) | ⏩ skipped | view |
| Configure build (wheeldeps) | ✅ success | view |
| Create CUDA Quantum installer | ⏩ skipped | view |
| Create Docker images | ⏩ skipped | view |
| Create Python metapackages | ⏩ skipped | view |
| Create Python wheels | ⏩ skipped | view |
| Gen code coverage | ⏩ skipped | view |
| Load dependencies (amd64, gcc12) / Caching | ✅ success | view |
| Load dependencies (amd64, gcc12) / Finalize | ✅ success | view |
| Load dependencies (amd64, gcc12) / Metadata | ✅ success | view |
| Load dependencies (amd64, llvm) / Caching | ✅ success | view |
| Load dependencies (amd64, llvm) / Finalize | ✅ success | view |
| Load dependencies (amd64, llvm) / Metadata | ✅ success | view |
| Load dependencies (arm64, gcc12) / Caching | ✅ success | view |
| Load dependencies (arm64, gcc12) / Finalize | ✅ success | view |
| Load dependencies (arm64, gcc12) / Metadata | ✅ success | view |
| Load dependencies (arm64, llvm) / Caching | ✅ success | view |
| Load dependencies (arm64, llvm) / Finalize | ✅ success | view |
| Load dependencies (arm64, llvm) / Metadata | ✅ success | view |
| Load source build cache | ⏩ skipped | view |
| Load wheel dependencies (amd64, 12.6) / Caching | ✅ success | view |
| Load wheel dependencies (amd64, 12.6) / Finalize | ✅ success | view |
| Load wheel dependencies (amd64, 12.6) / Metadata | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Caching | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Finalize | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Metadata | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Caching | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Finalize | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Metadata | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Caching | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Finalize | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Metadata | ✅ success | view |
| Prepare cache clean-up | ❔ in_progress | view |
| Retrieve PR info | ✅ success | view |
✅ Required checks (6/6) — declared in .github/required-checks.yml for push
| Required check | Status | Link |
|---|---|---|
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Python) | ✅ success | view |
Run rotation legalization between two commutation-aware simplification phases. This preserves pre-legalization rotation combining and post-legalization cleanup without invalidating the interaction index after every expanding rewrite. Add focused coverage for cleanup between a legalized rotation sequence and an existing adjacent gate. Verified with the QuakeSimplify and commutation-aware focused suites on the combined FTQC candidate. Signed-off-by: Thomas Alexander <talexander@nvidia.com>
1568ec9 to
e80d6e7
Compare
atgeller
left a comment
There was a problem hiding this comment.
I'd like some more clarification about the details of the interaction index, there were some confusing/unexpected things that I saw.
| } | ||
|
|
||
| // Build one ordered interaction stream for each logical qubit. A search on `q0` | ||
| // can then skip an `X(q1)` and visit only `S(q0), H(q0)`. Hard barriers split |
There was a problem hiding this comment.
Could you clarify here - what is a hard barrier? Is there such a thing as a "soft" barrier?
| // takes the erased operation's position. The rewrite listener removes erased | ||
| // pointers and rebuilds the index after insertions that cannot be placed | ||
| // safely. | ||
| class cudaq::quake::detail::LogicalQubitInteractionIndex { |
There was a problem hiding this comment.
I think it would be helpful to have an explanation of how the streams are used for the analysis, even if briefly, here.
| bool hasPendingInsertions() const { return !pendingInsertions.empty(); } | ||
|
|
||
| PriorInteractionWalkResult | ||
| walk(Operation *anchor, Operation *inclusiveUpperBound, |
There was a problem hiding this comment.
I'm lost by what walk is doing. Can you add a description/contract?
It seems to return conclusive in any case it can find the walk bounds, regardless of the visitor function which is not what I would expect.
| } | ||
|
|
||
| PriorInteractionWalkResult | ||
| walkSingleQubitStream(const Segment &segment, QubitId qubitId, |
There was a problem hiding this comment.
It seems this method always returns PriorInteractionWalkResult::conclusive?
| std::move(*replacementSupport)}; | ||
| removeInteractions(operation, position->second); | ||
| positions.erase(position); | ||
| auto [inserted, didInsert] = |
There was a problem hiding this comment.
Nit: on 831 this is [position, inserted], but here it is [inserted, didInsert]. Inconsistent naming
Rename the index around the operations stored for each logical qubit and define segment boundaries as unconditional indexed-search stops. Replace the two-value walk result with a boolean completion contract, and document traversal order, fallback behavior, query-specific stops, and ordinal maintenance. Verified by building cudaq-opt and OptimizerUnitTests. All 41 optimizer unit tests and both focused lit tests pass. The issue reproducer output hashes remain unchanged. Signed-off-by: Thomas Alexander <talexander@nvidia.com>
Fixes #5268 and #5269.
QuakeSimplifycurrently scans every earlier operation when looking for a gate to combine. For example,Previously, the search checked every
q1gate before reaching theH(q0)that stops it (as wires cannot always be followed due to a bunch of other issues noted below). Repeating this across a growing block can produce quadratic scan work.This change builds a lazy interaction index for each known virtual qubit to operation. This is done on segment boundaries and when I say "virtual" qubit I mean a qubit that is statically knowable as equivalent to itself in the wire-flow chain:
The
q0search now skips theq1stream and reaches the sameH(q0)immediately. Unsupported operations form segment boundaries, while unresolved qubit identities retain the existing block-order search.All of the other commutation machinery has been kept the same.
Performance:
CommutationAwareRewriteMatcher#5268 at 2,048 placements: 1.91 s → 0.038 sQuakeSimplifyscans across disjoint quantum support #5269 search work no longer grows with the sizeI tried to keep it as simple as possible but it was still more LOC than I would have liked. A large portion of the difficulty with all of this work is we really can't "just" follow value form as there are loads of special cases of aliasing, reference form, unknown qubit values, etc., making the wire semantics non-trivial and causing the analysis "segment" within a block. Perhaps the right fix would be to simplify the IR/make the value form more strict, however, I don't think this is possible right now.
To review I recommend looking at it in this order:
find_nearest: unchanged matching rules and the handoff to indexed traversal.LogicalQubitInteractionIndex: operation classification, segments, and ordered streams.